import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames  from 'classnames';
import {ConfigProvider, defaultIconPrefixCls, defaultPrefixCls} from '../config-provider/configProvider';
import OriginNotification from '../origin-notification';
import type {
  NoticeContent,
  NotificationInstance as OriginNotificationInstance
} from '../origin-notification/Notification';
import Icon from '../icon' ;
import './style';

/** START 自定义图标 */
// 这样能用上本组件库Icon样式前缀
const SuccessIcon = (props:any) => <Icon component={CheckCircleFilled} {...props}/> ;
const InfoIcon = (props:any) => <Icon component={InfoCircleFilled} {...props}/> ;
const ErrorIcon = (props:any) => <Icon component={CloseCircleFilled} {...props}/> ;
const WarningIcon = (props:any) => <Icon component={ExclamationCircleFilled} {...props}/> ;
const LoadingIcon = (props:any) => <Icon component={LoadingOutlined} {...props}/>
/** END 自定义图标 */

/** START 全局变量声明(模块内部使用) **/
let messageInstance: OriginNotificationInstance | null;
let defaultDuration = 3;
let defaultTop: number;
let key = 1;
let localPrefixCls = `${defaultPrefixCls}-message` ;
let transitionName = 'move-up' ;
let hasTransitionName = false ;
let getContainer: () => HTMLElement ;
let maxCount: number ;
let rtl = false ;
/** END 全局变量声明(模块内部使用) **/

// 当前模块和useMessage模块中使用
export function getKeyThenIncreaseKey() {
  return key++ ;
}

/** START message.config的参数接口 **/
export interface ConfigOptions {
  top?: number ;  //消息距离顶部的位置
  duration?: number ; //默认自动关闭延时，单位秒
  prefixCls?: string ;  //消息节点的 className 前缀 (默认为au-message)
  getContainer?: () => HTMLElement ; //配置渲染节点的输出位置 ( 默认为() => document.body	)
  transitionName?: string ;
  maxCount?: number ; //最大显示数, 超过限制时，最早的消息会被自动关闭
  rtl?: boolean ; //是否开启 RTL 模式
}
/** END **/

/** START
  其实就是message.config【对外以message.config暴露】
  作用:
    设置全局变量的值【更改配置项】
  调用位置:
    等价于message.config
**/
function setMessageConfig(options: ConfigOptions) {
  if (options.top !== undefined) {
    defaultTop = options.top;
    messageInstance = null ; //删除已存在的messageInstance
  }
  if (options.duration !== undefined) {
    defaultDuration = options.duration ;
  }
  if (options.prefixCls !== undefined) {
    localPrefixCls = options.prefixCls ;
  }
  if (options.getContainer !== undefined) {
    getContainer = options.getContainer;
    messageInstance = null ; //删除已存在的messageInstance
  }
  if (options.transitionName !== undefined) {
    transitionName = options.transitionName ;
    messageInstance = null ; //删除已存在的messageInstance
    hasTransitionName = true ; //打表记
  }
  if (options.maxCount !== undefined) {
    maxCount = options.maxCount ;
    messageInstance = null ; //删除已存在的messageInstance
  }
  if(options.rtl !== undefined) {
    rtl = options.rtl ;
  }
}
/** END **/

function getOriginNotificationInstance(
  args: ArgsProps ,
  callback: (originNotificationInstanceInfo:{
    prefixCls: string ; //组件样式前缀,如: au-message
    rootPrefixCls: string ; //全局样式前缀,如: au
    iconPrefixCls: string ;
    instance: OriginNotificationInstance ;
  }) => void ,
) {
  const {
    prefixCls: customizePrefixCls , //用户通过message.config设置的样式前缀
    getPopupContainer: getContextPopupContainer
  } = args ;
  const prefixCls = customizePrefixCls || localPrefixCls ;
  const rootPrefixCls = defaultPrefixCls;
  const iconPrefixCls = defaultIconPrefixCls;
  if (messageInstance) { //若messageInstance已存在,则直接callback
    callback({
      prefixCls,
      rootPrefixCls,
      iconPrefixCls,
      instance:messageInstance,
    });
    return ;
  }

  // 若messageInstance不存在,则进行以下逻辑进行实例化并callback
  const instanceConfig = {
    prefixCls,
    transitionName: hasTransitionName ? transitionName : `${rootPrefixCls}-${transitionName}` ,
    style: { top: defaultTop } , //样式覆盖
    getContainer: getContainer || getContextPopupContainer ,
    maxCount ,
  };

  OriginNotification.newInstance(instanceConfig,(instance:OriginNotificationInstance)=>{
    if (messageInstance) {  //再次检查,若messageInstance已存在,则直接callback
      callback({ prefixCls,rootPrefixCls,iconPrefixCls,instance: messageInstance });
      return;
    }
    messageInstance = instance ; //绑定实例

    callback({ prefixCls,rootPrefixCls,iconPrefixCls,instance:messageInstance });
  });

}

export interface MessageType extends PromiseLike<any> {
  (): void;
}


/* methods 相关 START */
const typeToIcon = {
  info: InfoIcon,
  success: SuccessIcon,
  error: ErrorIcon,
  warning: WarningIcon,
  loading: LoadingIcon,
};

//关于keyof typeof用法的解释https://stackoverflow.com/questions/55377365/what-does-keyof-typeof-mean-in-typescript
export type NoticeType = keyof typeof typeToIcon;

//union of literal types "info"|"success"|"error"|"warning"|"loading"
export const typeList:NoticeType[] = Object.keys(typeToIcon) as NoticeType[];



/* methods 相关 END */

//message[method](config) 中的config对象属性
export interface ArgsProps {
  content: any;
  duration?:number ;
  type?: NoticeType ;
  prefixCls?: string ;
  rootPrefixCls?: string;
  getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
  onClose?: () => void;
  icon?: React.ReactNode ;
  key?: string | number ;
  style?: React.CSSProperties ;
  className?: string ;
  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
}

// 将一堆输入转化为NoticeContent的格式,用于OiginNotificationInstance.notice(noticeProps)方法
function getOriginNoticeProps(
  args: ArgsProps,
  prefixCls: string,
  iconPrefixCls?: string,
): NoticeContent {
  const duration = args.duration !== undefined ? args.duration : defaultDuration ;
  const IconComponent = typeToIcon[args.type!];
  const messageClass = classNames(
    `${prefixCls}-custom-content`,
    {
      [`${prefixCls}-custom-content-${args.type}`]: args.type, //用于apusic-ui样式规范(覆盖到notice-content的样式)
      [`${prefixCls}-${args.type}`]: args.type,  //搭配css使用
      [`${prefixCls}-rtl`]: rtl === true,
    }
  );
  return {
    key: args.key,
    duration,
    style: args.style || {},
    className: args.className,
    content: (
      <ConfigProvider iconPrefixCls={iconPrefixCls}>
        <div className={messageClass}>
          {args.icon || (IconComponent && <IconComponent/>)}
          <span>{args.content}</span>
        </div>
      </ConfigProvider>
    ),
    closable:true,
    onClose: args.onClose,
    onClick: args.onClick,
  };
}


function notice(args: ArgsProps): any {
  const target = args.key || getKeyThenIncreaseKey();

  getOriginNotificationInstance(args, ({ prefixCls, iconPrefixCls, instance })=>{
    instance.notice(
      getOriginNoticeProps({
        ...args,
        key: target,
        onClose:()=>{}
      },prefixCls,iconPrefixCls)
    );
  });
}

export interface MessageInstance {
  open(args: ArgsProps): MessageType;
}

export interface MessageApi extends MessageInstance {
  config(options: ConfigOptions): void;
  destroy(messageKey?: React.Key): void;
}

const api: MessageApi = {
  open: notice ,
  config: setMessageConfig ,
  destroy(messageKey?: React.Key) {
    if(messageInstance){
      if(messageKey){
        messageInstance.removeNotice(messageKey);
      }else{
        messageInstance.destroy();
        messageInstance = null;
      }
    }
  },
}

type ConfigContent = React.ReactNode;
type ConfigDuration = number | (() => void);
type JointContent = ConfigContent | ArgsProps;
export type ConfigOnClose = () => void;

function isArgsProps(content: JointContent): content is ArgsProps {
  return (
    Object.prototype.toString.call(content) === '[object Object]' &&
    !!(content as ArgsProps).content
  );
}

export function attachTypeApi(originalApi: MessageApi, type: NoticeType) {
  originalApi[type] = (
    content: JointContent,
    duration?: ConfigDuration,
    onClose?: ConfigOnClose,
  ) => {
    if(isArgsProps(content)){
      return originalApi.open({type,...content});
    }
    if(typeof duration === 'function'){
      onClose = duration ;
      duration = undefined ;
    }
    return originalApi.open({
      content,
      duration,
      type,
      onClose
    });
  }
}

typeList.forEach(type => attachTypeApi(api,type));

export default api ;